From 76bacfde6e5305021c765ae690972efb1832682d Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Wed, 15 Jun 2016 23:02:11 -0400 Subject: [PATCH] style cascade: Allow cascades with more ancestors Previously a style cascade's parent could not have a parent itself. That represented the two levels at which you could add a style provider: at the screen level, with gtk_style_context_add_provider_for_screen(), and at the style context level, with gtk_style_context_add_provider(). This commit changes no functionality, but this change will be necessary for adding style providers in the future that apply to a subtree of the widget tree. It relaxes the requirement that a style cascade's parent must not have a parent, since in the future a style context may be affected by any number of parent widgets' style contexts. https://bugzilla.gnome.org/show_bug.cgi?id=751409 --- gtk/gtkstylecascade.c | 99 ++++++++------- testsuite/gtk/stylecontext.c | 230 +++++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+), 41 deletions(-) diff --git a/gtk/gtkstylecascade.c b/gtk/gtkstylecascade.c index 6afa40c040..2c890f536d 100644 --- a/gtk/gtkstylecascade.c +++ b/gtk/gtkstylecascade.c @@ -27,8 +27,9 @@ typedef struct _GtkStyleCascadeIter GtkStyleCascadeIter; typedef struct _GtkStyleProviderData GtkStyleProviderData; struct _GtkStyleCascadeIter { - int parent_index; /* pointing at last index that was returned, not next one that should be returned */ - int index; /* pointing at last index that was returned, not next one that should be returned */ + int n_cascades; + int *cascade_index; /* each one points at last index that was returned, */ + /* not next one that should be returned */ }; struct _GtkStyleProviderData @@ -42,56 +43,57 @@ static GtkStyleProvider * gtk_style_cascade_iter_next (GtkStyleCascade *cascade, GtkStyleCascadeIter *iter) { - if (iter->parent_index > 0) - { - if (iter->index > 0) - { - GtkStyleProviderData *data, *parent_data; + GtkStyleCascade *cas; + int ix, highest_priority_index = 0; + GtkStyleProviderData *highest_priority_data = NULL; - data = &g_array_index (cascade->providers, GtkStyleProviderData, iter->index - 1); - parent_data = &g_array_index (cascade->parent->providers, GtkStyleProviderData, iter->parent_index - 1); + for (cas = cascade, ix = 0; ix < iter->n_cascades; cas = cas->parent, ix++) + { + if (iter->cascade_index[ix] <= 0) + continue; - if (data->priority >= parent_data->priority) - { - iter->index--; - return data->provider; - } - else - { - iter->parent_index--; - return parent_data->provider; - } - } - else + GtkStyleProviderData *data = &g_array_index (cas->providers, + GtkStyleProviderData, + iter->cascade_index[ix] - 1); + if (highest_priority_data == NULL || data->priority > highest_priority_data->priority) { - iter->parent_index--; - return g_array_index (cascade->parent->providers, GtkStyleProviderData, iter->parent_index).provider; + highest_priority_index = ix; + highest_priority_data = data; } } - else + + if (highest_priority_data != NULL) { - if (iter->index > 0) - { - iter->index--; - return g_array_index (cascade->providers, GtkStyleProviderData, iter->index).provider; - } - else - { - return NULL; - } + iter->cascade_index[highest_priority_index]--; + return highest_priority_data->provider; } + return NULL; } static GtkStyleProvider * gtk_style_cascade_iter_init (GtkStyleCascade *cascade, GtkStyleCascadeIter *iter) { - iter->parent_index = cascade->parent ? cascade->parent->providers->len : 0; - iter->index = cascade->providers->len; + GtkStyleCascade *cas = cascade; + int ix; + + iter->n_cascades = 1; + while ((cas = cas->parent) != NULL) + iter->n_cascades++; + + iter->cascade_index = g_new (int, iter->n_cascades); + for (cas = cascade, ix = 0; ix < iter->n_cascades; cas = cas->parent, ix++) + iter->cascade_index[ix] = cas->providers->len; return gtk_style_cascade_iter_next (cascade, iter); } +static void +gtk_style_cascade_iter_clear (GtkStyleCascadeIter *iter) +{ + g_free (iter->cascade_index); +} + static gboolean gtk_style_cascade_get_style_property (GtkStyleProvider *provider, GtkWidgetPath *path, @@ -112,9 +114,13 @@ gtk_style_cascade_get_style_property (GtkStyleProvider *provider, state, pspec, value)) - return TRUE; + { + gtk_style_cascade_iter_clear (&iter); + return TRUE; + } } + gtk_style_cascade_iter_clear (&iter); return FALSE; } @@ -141,9 +147,13 @@ gtk_style_cascade_get_settings (GtkStyleProviderPrivate *provider) settings = _gtk_style_provider_private_get_settings (GTK_STYLE_PROVIDER_PRIVATE (item)); if (settings) - return settings; + { + gtk_style_cascade_iter_clear (&iter); + return settings; + } } + gtk_style_cascade_iter_clear (&iter); return NULL; } @@ -164,7 +174,10 @@ gtk_style_cascade_get_color (GtkStyleProviderPrivate *provider, { color = _gtk_style_provider_private_get_color (GTK_STYLE_PROVIDER_PRIVATE (item), name); if (color) - return color; + { + gtk_style_cascade_iter_clear (&iter); + return color; + } } else { @@ -172,6 +185,7 @@ gtk_style_cascade_get_color (GtkStyleProviderPrivate *provider, } } + gtk_style_cascade_iter_clear (&iter); return NULL; } @@ -201,9 +215,13 @@ gtk_style_cascade_get_keyframes (GtkStyleProviderPrivate *provider, keyframes = _gtk_style_provider_private_get_keyframes (GTK_STYLE_PROVIDER_PRIVATE (item), name); if (keyframes) - return keyframes; + { + gtk_style_cascade_iter_clear (&iter); + return keyframes; + } } + gtk_style_cascade_iter_clear (&iter); return NULL; } @@ -236,6 +254,7 @@ gtk_style_cascade_lookup (GtkStyleProviderPrivate *provider, g_warn_if_reached (); } } + gtk_style_cascade_iter_clear (&iter); } static void @@ -303,8 +322,6 @@ _gtk_style_cascade_set_parent (GtkStyleCascade *cascade, { gtk_internal_return_if_fail (GTK_IS_STYLE_CASCADE (cascade)); gtk_internal_return_if_fail (parent == NULL || GTK_IS_STYLE_CASCADE (parent)); - if (parent) - gtk_internal_return_if_fail (parent->parent == NULL); if (cascade->parent == parent) return; diff --git a/testsuite/gtk/stylecontext.c b/testsuite/gtk/stylecontext.c index cf0e0a2bf6..6ae7ff9c61 100644 --- a/testsuite/gtk/stylecontext.c +++ b/testsuite/gtk/stylecontext.c @@ -1,5 +1,12 @@ #include +typedef struct { + GtkStyleContext *context; + GtkCssProvider *blue_provider; + GtkCssProvider *red_provider; + GtkCssProvider *green_provider; +} PrioritiesFixture; + static void test_parse_selectors (void) { @@ -362,6 +369,213 @@ test_style_classes (void) g_object_unref (context); } +static void +test_style_priorities_setup (PrioritiesFixture *f, + gconstpointer unused) +{ + GError *error = NULL; + f->blue_provider = gtk_css_provider_new (); + f->red_provider = gtk_css_provider_new (); + f->green_provider = gtk_css_provider_new (); + f->context = gtk_style_context_new (); + GtkWidgetPath *path = gtk_widget_path_new (); + + gtk_css_provider_load_from_data (f->blue_provider, "* { color: blue; }", -1, &error); + g_assert_no_error (error); + gtk_css_provider_load_from_data (f->red_provider, "* { color: red; }", -1, &error); + g_assert_no_error (error); + gtk_css_provider_load_from_data (f->green_provider, "* { color: green; }", -1, &error); + g_assert_no_error (error); + + gtk_widget_path_append_type (path, GTK_TYPE_WINDOW); + gtk_style_context_set_path (f->context, path); + + gtk_widget_path_free (path); +} + +static void +test_style_priorities_teardown (PrioritiesFixture *f, + gconstpointer unused) +{ + g_object_unref (f->blue_provider); + g_object_unref (f->red_provider); + g_object_unref (f->green_provider); + g_object_unref (f->context); +} + +static void +test_style_priorities_equal (PrioritiesFixture *f, + gconstpointer unused) +{ + GdkRGBA color, ref_color; + + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (f->blue_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + + /* When style providers are added to the screen as well as the style context + the one specific to the style context should take priority */ + gdk_rgba_parse (&ref_color, "red"); + gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context), + &color); + + g_assert_true (gdk_rgba_equal (&ref_color, &color)); +} + +static void +test_style_priorities_screen_only (PrioritiesFixture *f, + gconstpointer unused) +{ + GdkRGBA color, ref_color; + + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (f->blue_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + + gdk_rgba_parse (&ref_color, "blue"); + gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context), + &color); + + g_assert_true (gdk_rgba_equal (&ref_color, &color)); +} + +static void +test_style_priorities_context_only (PrioritiesFixture *f, + gconstpointer unused) +{ + GdkRGBA color, ref_color; + + gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + + gdk_rgba_parse (&ref_color, "red"); + gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context), + &color); + + g_assert_true (gdk_rgba_equal (&ref_color, &color)); +} + +static void +test_style_priorities_screen_higher (PrioritiesFixture *f, + gconstpointer unused) +{ + GdkRGBA color, ref_color; + + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (f->blue_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER + 1); + gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + + gdk_rgba_parse (&ref_color, "blue"); + gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context), + &color); + + g_assert_true (gdk_rgba_equal (&ref_color, &color)); +} + +static void +test_style_priorities_context_higher (PrioritiesFixture *f, + gconstpointer unused) +{ + GdkRGBA color, ref_color; + + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (f->blue_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER + 1); + + gdk_rgba_parse (&ref_color, "red"); + gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context), + &color); + + g_assert_true (gdk_rgba_equal (&ref_color, &color)); +} + +static void +test_style_priorities_two_screen (PrioritiesFixture *f, + gconstpointer unused) +{ + GdkRGBA color, ref_color; + + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (f->blue_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (f->red_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER + 1); + + gdk_rgba_parse (&ref_color, "red"); + gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context), + &color); + + g_assert_true (gdk_rgba_equal (&ref_color, &color)); +} + +static void +test_style_priorities_two_context (PrioritiesFixture *f, + gconstpointer unused) +{ + GdkRGBA color, ref_color; + + gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->blue_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER + 1); + + gdk_rgba_parse (&ref_color, "red"); + gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context), + &color); + + g_assert_true (gdk_rgba_equal (&ref_color, &color)); +} + +static void +test_style_priorities_three_screen_higher (PrioritiesFixture *f, + gconstpointer unused) +{ + GdkRGBA color, ref_color; + + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (f->blue_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (f->green_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER + 1); + gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + + gdk_rgba_parse (&ref_color, "green"); + gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context), + &color); + + g_assert_true (gdk_rgba_equal (&ref_color, &color)); +} + +static void +test_style_priorities_three_context_higher (PrioritiesFixture *f, + gconstpointer unused) +{ + GdkRGBA color, ref_color; + + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (f->blue_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->green_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER + 1); + + gdk_rgba_parse (&ref_color, "green"); + gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context), + &color); + + g_assert_true (gdk_rgba_equal (&ref_color, &color)); +} + int main (int argc, char *argv[]) { @@ -376,5 +590,21 @@ main (int argc, char *argv[]) g_test_add_func ("/style/widget-path-parent", test_widget_path_parent); g_test_add_func ("/style/classes", test_style_classes); +#define ADD_PRIORITIES_TEST(path, func) \ + g_test_add ("/style/priorities/" path, PrioritiesFixture, NULL, test_style_priorities_setup, \ + (func), test_style_priorities_teardown) + + ADD_PRIORITIES_TEST ("equal", test_style_priorities_equal); + ADD_PRIORITIES_TEST ("screen-only", test_style_priorities_screen_only); + ADD_PRIORITIES_TEST ("context-only", test_style_priorities_context_only); + ADD_PRIORITIES_TEST ("screen-higher", test_style_priorities_screen_higher); + ADD_PRIORITIES_TEST ("context-higher", test_style_priorities_context_higher); + ADD_PRIORITIES_TEST ("two-screen", test_style_priorities_two_screen); + ADD_PRIORITIES_TEST ("two-context", test_style_priorities_two_context); + ADD_PRIORITIES_TEST ("three-screen-higher", test_style_priorities_three_screen_higher); + ADD_PRIORITIES_TEST ("three-context-higher", test_style_priorities_three_context_higher); + +#undef ADD_PRIORITIES_TEST + return g_test_run (); } -- 2.30.2